package com.revai.engine.basic.service.impl;

import com.revai.engine.basic.service.ClassCacheService;
import com.revai.engine.basic.service.ReflectService;
import com.revai.engine.basic.service.TableService;
import com.revai.engine.basic.util.ServiceUtil;
import com.revai.engine.basic.util.StrUtils;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import io.swagger.annotations.ApiModelProperty;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.revai.engine.basic.core.AuditStateEnum;
import com.revai.engine.basic.core.TableField;
import com.revai.engine.basic.mapper.TableMapper;
import org.yaml.snakeyaml.Yaml;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class TableServiceImpl implements TableService {

  private final Logger logger = LoggerFactory.getLogger(TableServiceImpl.class);

  private static final String PAGE = "page";

  private static final String TABLE = "Table";

  @Autowired
  private ApplicationContext applicationContext;

  @Autowired
  private ClassCacheService classCache;

  @Autowired
  private ReflectService reflectService;

  @Autowired
  private Environment environment;

  /**
   * search查询数据列表
   *
   * @throws Exception
   */
  @Override
  public List<Map<String, Object>> getTableData(JSONObject query) throws Exception {
    // logger.info(query.toString());

    // 1.解析json字符串，json字符串里有个数据库表参数，
    String source = query.getStr("source");

    // 2.表名，划分数组字符串
    String tableName = StrUtils.getTableName(source);
    // 同上
    String modelName = StrUtils.getMedelName(source);

    // 3.获取conditions里的参数
    JSONObject conditions = query.getJSONObject("conditions");

    // 4.为空新建一个JSONObject对象，
    if (conditions == null) {
      conditions = new JSONObject();
    }

    // 5.缓冲区里面查找对应的Service类

    // 如果有自定义service，使用自定义service
    Class<?> serviceClass = classCache.getServiceClass(tableName);

    JSONArray ids = conditions.getJSONArray("ids");
    List<Long> queryIds = new ArrayList<>();

    if (ids != null && ids.size() > 0) {
      for (int i = 0; i < ids.size(); i++) {
        queryIds.add(ids.getLong(i));
      }
    }

    // 如果有自定义service，使用自定义service
    if (serviceClass != null && queryIds.size() == 0) {
      Object serviceBean = applicationContext.getBean(serviceClass);
      // 7.HashMap缓冲区
      Class<?> modelClass = classCache.getModelClass(tableName);

      // 8.获取页数、尺寸、排序方式(默认降序)
      Integer page = query.getInt("page");
      if (page != null) {
        Integer size = query.getInt("size");
        String order = query.getStr("order");
        if (order == null || order == "") {
          order = "id desc";
        }
        return ReflectUtil.invoke(serviceBean, "list", conditions.toBean(modelClass), size == null ? 10 : size, page,
            order);
      } else {
        // 9.如果不等于null,反射递归调用里面的方法
        return ReflectUtil.invoke(serviceBean, "list", conditions.toBean(modelClass));
      }
    }
    // 给page和size设置默认值
    if (query.getInt(PAGE) != null) {
      PageHelper.startPage(query.getInt("page"), query.getInt("size") == null ? 10 : query.getInt("size"));
    }
    // 获取排序方式(默认为降序)
    String orderBy = query.getStr("order");
    PageHelper.orderBy(StringUtils.isBlank(orderBy) ? "id desc" : orderBy);

    return reflectService.query(modelName, conditions);
  }

  @Override
  public Object getTableItem(JSONObject query) throws Exception {
    // logger.info(query.toString());
    String source = query.getStr("source");
    Object isHistory = query.get("history");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    Long id = query.getLong("id");
    // 如果有自定义service，使用自定义service
    Class<?> serviceClass = classCache.getServiceClass(tableName);
    Object result;
    if (serviceClass != null) {
      Object serviceBean = applicationContext.getBean(serviceClass);
      result = ReflectUtil.invoke(serviceBean, "detail", id);
    } else {
      result = reflectService.getItem(modelName, id);
    }
    if (result == null) {
      throw new Exception("数据不存在");
    }
    // 如果有历史表
    if (isHistory != null && isHistory.equals(true)) {
      Integer approveStatus = ReflectUtil.invoke(result, "getApprovalStatus");
      // 如果是审批中，从历史表取数据
      if (approveStatus != null && approveStatus == AuditStateEnum.IN_APPROVAL.getCode()) {
        return reflectService.getHistoryByDataId(tableName, id);
      }
    }
    return result;
  }

  /**
   * 新增数据
   *
   * @throws Exception
   */
  @Override
  public Long addTableData(JSONObject entry) throws Exception {
    // logger.info(entry.toString());
    String source = entry.getStr("source");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    JSONObject form = entry.getJSONObject("fields");
    // // 如果有自定义service，使用自定义service
    // Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    // // 获取 UserDetails 对象
    // UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    // // 获取用户名
    // String username = userDetails.getUsername();
    // form.put("createdBy", username);
    // form.put("updateBy", username);
    Class<?> serviceClass = classCache.getServiceClass(tableName);
    if (serviceClass != null) {
      Object serviceBean = applicationContext.getBean(serviceClass);
      Class<?> modelClass = classCache.getModelClass(tableName);
      Object modelValue = form.toBean(modelClass);
      ReflectUtil.invoke(serviceBean, "create", modelValue);
      // 新增成功后，返回id
      return ReflectUtil.invoke(modelValue, "getId");
    }
    return reflectService.add(modelName, form);
  }

  /**
   * 更新数据
   *
   * @throws Exception
   */
  @Override
  public Integer updateTableData(JSONObject entry) throws Exception {
    // logger.info(entry.toString());
    String source = entry.getStr("source");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    JSONObject form = entry.getJSONObject("fields");
    // 如果有自定义service，使用自定义service
    Class<?> serviceClass = classCache.getServiceClass(tableName);
    if (serviceClass != null) {
      Object serviceBean = applicationContext.getBean(serviceClass);
      Class<?> modelClass = classCache.getModelClass(tableName);
      return ReflectUtil.invoke(serviceBean, "update", form.toBean(modelClass));
    }
    return reflectService.update(modelName, form);
  }

  /**
   * 提交审批
   *
   * @throws Exception
   */
  @Override
  public Integer auditTableData(JSONObject entry, String authToken) throws Exception {
    // logger.info(entry.toString());
    String source = entry.getStr("source");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    JSONObject form = entry.getJSONObject("fields");
    return reflectService.audit(modelName, form, authToken);
  }

  /**
   * 执行审批
   *
   * @throws Exception
   */
  @Override
  public Integer approveTableData(JSONObject entry, String authToken) throws Exception {
    // logger.info(entry.toString());
    String source = entry.getStr("source");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    JSONObject form = entry.getJSONObject("fields");
    return reflectService.approve(modelName, form, authToken);
  }

  /**
   * 删除数据
   *
   * @throws Exception
   */
  @Override
  public Integer deleteTableData(JSONObject entry) throws Exception {
    // logger.info(entry.toString());
    String source = entry.getStr("source");
    String tableName = StrUtils.getTableName(source);
    String modelName = StrUtils.getMedelName(source);
    JSONArray ids = entry.getJSONArray("id");
    Integer count = 0;
    // 如果有自定义service，使用自定义service
    Class<?> serviceClass = classCache.getServiceClass(tableName);
    if (serviceClass != null) {
      // 查出serviceClass类获取serviceBean
      Object serviceBean = applicationContext.getBean(serviceClass);

      // 遍历id数组
      for (int i = 0; i < ids.size(); i++) {
        Long id = ids.getLong(i);
        // 传入这三个参数serviceBean、delete关键字,id值 不断地获取反射里面的方法
        Integer r = ReflectUtil.invoke(serviceBean, "delete", id);
        count += r;
      }
    } else {
      for (int i = 0; i < ids.size(); i++) {
        Long id = ids.getLong(i);

        // 如果serviceBean调用反射的delete方法去删除它
        count += reflectService.delete(modelName, id);
      }
    }
    return count;
  }

  /**
   * 查询字典数据
   *
   * @throws Exception
   */
  @Override
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public Object getTableValues(JSONObject query) throws Exception {
    String source = query.getStr("source");

    String[] sources = source.split("\\.");
    if (sources[0].endsWith(TABLE)) {
      String tableName = sources[1];
      String fieldName = sources[2];
      query.put("source", tableName);
      List<Map<String, Object>> list = getTableData(query);
      List results = new ArrayList<>();
      for (Object item : list) {
        Map<String, Object> result = new HashMap<>();
        if (item instanceof Map) {
          result.put("name", ((Map) item).get(fieldName));
          result.put("key", ((Map) item).get("id"));
        } else {
          result.put("name", ReflectUtil.invoke(item, "get" + StrUtils.toFieldCamelCase(fieldName)));
          result.put("key", ReflectUtil.invoke(item, "get" + StrUtils.toFieldCamelCase("id")));
        }
        results.add(result);
      }

      Map page = new HashMap<>();
      PageInfo pageInfo = new PageInfo(list);
      page.put("DataCount", pageInfo.getTotal());
      page.put("NowPage", pageInfo.getPageNum());
      page.put("OnePageCount", pageInfo.getSize());
      page.put("PageCount", pageInfo.getPages());
      page.put("StartIndex", pageInfo.getStartRow());
      page.put("OrderBy", null);

      Map res = new HashMap<>();
      res.put("Page", page);
      res.put("values", results);

      return res;
    } else if (StringUtils.isNoneBlank(source)) {
      String tableName = source;
      query.put("source", tableName);
      List<Map<String, Object>> list = getTableData(query);

      Map page = new HashMap<>();
      PageInfo pageInfo = new PageInfo(list);
      page.put("DataCount", pageInfo.getTotal());
      page.put("NowPage", pageInfo.getPageNum());
      page.put("OnePageCount", pageInfo.getSize());
      page.put("PageCount", pageInfo.getPages());
      page.put("StartIndex", pageInfo.getStartRow());
      page.put("OrderBy", null);

      Map res = new HashMap<>();
      res.put("Page", page);
      res.put("values", list);

      return res;
    } else {
      return new HashMap<>();
    }
  }

  /**
   * 根据ids查询出数据
   *
   * @throws Exception
   */
  @Override
  public List<Object> getForeignValues(JSONObject query) throws Exception {
    String source = query.getStr("source");
    JSONArray ids = query.getJSONArray("ids");
    if (ids != null && ids.size() > 0) {
      List<Long> queryIds = new ArrayList<>();
      for (int i = 0; i < ids.size(); i++) {
        queryIds.add(ids.getLong(i));
      }
      List<Object> list = reflectService.queryByIds(source, queryIds);
      return list;
    }
    return new ArrayList<>();
  }

  // 检查是否是开发环境
  private boolean isDevelopment() {
    for (String profile : environment.getActiveProfiles()) {
      if ("dev".equalsIgnoreCase(profile)) {
        return true;
      }
    }
    return false;
  }

  private static void writeDataRow(Object obj, Row row) throws IllegalAccessException {
    Field[] fields = obj.getClass().getDeclaredFields();
    int columnCount = 0;
    for (Field field : fields) {
      field.setAccessible(true);
      Object value = field.get(obj);
      Cell cell = row.createCell(columnCount++);
      if (value instanceof String) {
        cell.setCellValue((String) value);
      } else if (value instanceof Integer) {
        cell.setCellValue((Integer) value);
      } else if (value instanceof Double) {
        cell.setCellValue((Double) value);
      }
    }
  }

  @Override
  public void exportExcelToOutputStream(JSONObject requestBody, OutputStream out) throws Exception {
    // 1. 创建工作簿
    XSSFWorkbook workbook = new XSSFWorkbook();
    // 2. 创建工作表
    XSSFSheet sheet = workbook.createSheet("Sheet1");
    // 3. 模拟待写入数据

    List<Map<String, Object>> list = getTableData(requestBody);

    String tableName = requestBody.getStr("source");
    Class<?> modelClass = classCache.getModelClass(tableName);

    // 遍历查询结果数据
    for (int i = 0; i < list.size(); i++) {
      // bean对象
      Object obj = list.get(i);

      Field[] fields = modelClass.getDeclaredFields();

      // 第一行写表头，todo读取requestBody传入的meta配置，改成中文和判断只导出display: true的字段
      if (i == 0) {
        XSSFRow firstRow = sheet.createRow(i);
        int cellNum = 0;
        for (int j = 0; j < fields.length - 1; j++) {
          Field field = fields[j];
          XSSFCell cell = firstRow.createCell(cellNum++);
          if (field.isAnnotationPresent(ApiModelProperty.class)) {
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            cell.setCellValue(apiModelProperty.value());
          } else {
            cell.setCellValue(field.getName());
          }
        }
      }

      XSSFRow row = sheet.createRow(i + 1);
      int cellNum = 0;
      for (int j = 0; j < fields.length - 1; j++) {
        Field field = fields[j];
        System.err.println(field.getName());
        Object value = ReflectUtil.invoke(obj, getGetter(field.getName()));
        XSSFCell cell = row.createCell(cellNum++);
        if (value != null) {
          // 先全部当字符串处理，todo：需要通过判断类型写value
          cell.setCellValue(String.valueOf(value));
        }
      }
    }

    workbook.write(out);
    workbook.close();
  }

  /**
   * 根据字段key获取getter方法名
   * 
   * @param key
   * @return
   */
  private String getGetter(String key) {
    return "get" + key.substring(0, 1).toUpperCase() + key.substring(1);
  }
}
